package com.dukk.gis.tools.extensions.skewer;

import java.util.*;
import java.util.stream.Collectors;

/**
 * 调用者 通过挂接node穿串
 * @param <T>
 */
public class Skewer<T> {

    private T t;

    private List<LinkedList<Node<T>>> kebab = new ArrayList<>();

    private List<LinkedList<Node<T>>> kebabDirectBegin = new ArrayList<>();

    private List<LinkedList<Node<T>>> kebabDirectEnd = new ArrayList<>();

    private SeekerWay seekerWay;

    private Seeker<T> seeker;

    //避免穿到环 死循环了
    private Set<Long> hadSeekerObjSet = new HashSet<>();



    public Skewer(Seeker<T> seeker, SeekerWay seekerWay, T t){
        this.seeker = seeker;
        this.seekerWay = seekerWay;
        this.t = t;
    }

    public List<LinkedList<Node<T>>> searchKebab(Node<T> node) throws Exception {
        return searchKebab(node, null);
    }

    /**
     * 注意: 寻找假设 SeekerWay.POINT_FIRST 左侧方向
     *       SeekerWay.POINT_LAST 右侧方向
     *       SeekerWay.POINT_DOUBLE 双方向
     * @param node 开始节点
     * @param dataList 输入的整体数据(例如9个网格内的)，开始节点的数据，通过datalist里面寻找数据进行穿串
     * @return
     * @throws Exception
     */
    public List<LinkedList<Node<T>>> searchKebab(Node<T> node, List<T> dataList) throws Exception {

        //添加第一个节点
        Node<T> firstNode = node;

        this.hadSeekerObjSet.add(firstNode.getId());

        //开始节点
        LinkedList<Node<T>> parentLinkedList = new LinkedList<>();
        parentLinkedList.add(firstNode);


        if(this.seekerWay == SeekerWay.POINT_START){
            this.kebabDirectBegin.add(parentLinkedList);
            circleFind(parentLinkedList, t, dataList, node.getStartNode(),this.seekerWay);

            this.kebab.addAll(this.kebabDirectBegin);
        } else if (this.seekerWay == SeekerWay.POINT_END) {
            this.kebabDirectEnd.add(parentLinkedList);
            circleFind(parentLinkedList, t, dataList, node.getEndNode(),this.seekerWay);

            this.kebab.addAll(this.kebabDirectEnd);
        }else { //双方向找

            this.kebabDirectBegin.add(parentLinkedList);

            LinkedList<Node<T>> parentLinkedEndList = (LinkedList<Node<T>>)parentLinkedList.clone();

            this.kebabDirectEnd.add(parentLinkedEndList);

            circleFind(parentLinkedList, t, dataList, node.getStartNode(), SeekerWay.POINT_START);
            circleFind(parentLinkedEndList, t, dataList, node.getEndNode(), SeekerWay.POINT_END);

            //开始 结束 随机组合 sXn 中组合方式
            for(LinkedList<Node<T>> linkedListStart : this.kebabDirectBegin){

                for (LinkedList<Node<T>> linkedListEnd : this.kebabDirectEnd){

                    LinkedList<Node<T>> linkedListMerge = new LinkedList<>();

                    linkedListMerge.addAll(linkedListStart);
                    if(linkedListEnd.size() >= 2){
                        linkedListMerge.addAll(linkedListEnd.subList(1, linkedListEnd.size()));
                    }

                    this.kebab.add(linkedListMerge);
                }
            }

        }

        return this.kebab;
    }

    private void circleFind(LinkedList<Node<T>> parentLinkedList, T parentObj, List<T> dataList, long nextNode, SeekerWay seekerWay) throws Exception {

            List<Node<T>> nodeList = seeker.findNode(parentObj, dataList, nextNode);

            nodeList = nodeList.stream().filter(t->{
               if(this.hadSeekerObjSet.contains(t.getId())){
                   return false;
               }
               return true;
            }).collect(Collectors.toList());

            if(null != nodeList && nodeList.size() >= 1){

                if(nodeList.size() == 1){
                    Node<T> currentNode = nodeList.get(0);
                    if (!seeker.stopCondition(parentLinkedList,currentNode)){

                        this.hadSeekerObjSet.add(currentNode.getId());


                        if(seekerWay == SeekerWay.POINT_END){
                            parentLinkedList.addLast(currentNode);
                        }else {
                            parentLinkedList.addFirst(currentNode);
                        }


                        List<Long> currentNodes = new ArrayList<>(Arrays.asList(currentNode.getStartNode(), currentNode.getEndNode()));
                        currentNodes.remove(nextNode);

                        long currentNextNode = currentNodes.get(0);

                                //下面在接着寻找
                        circleFind(parentLinkedList, currentNode.getElement(), dataList,currentNextNode,seekerWay);
                    }

                }else{ //大于1

                    for(Node<T> currentNode : nodeList){ //裂变 1变N

                        //下面在接着寻找
                        if (!seeker.stopCondition(parentLinkedList,currentNode)){

                            this.hadSeekerObjSet.add(currentNode.getId());

                            LinkedList<Node<T>> childNodeLinkedList = (LinkedList<Node<T>>)parentLinkedList.clone();



                            if(seekerWay == SeekerWay.POINT_END){
                                childNodeLinkedList.addLast(currentNode);
                                this.kebabDirectEnd.add(childNodeLinkedList);
                            }else {

                                childNodeLinkedList.addFirst(currentNode);
                                this.kebabDirectBegin.add(childNodeLinkedList);
                            }

                            List<Long> currentNodes = new ArrayList<>(Arrays.asList(currentNode.getStartNode(), currentNode.getEndNode()));
                            currentNodes.remove(nextNode);

                            long currentNextNode = currentNodes.get(0);

                            //下面在接着寻找
                            circleFind(childNodeLinkedList, currentNode.getElement(),dataList,currentNextNode,seekerWay);
                        }

                    }

                    if(seekerWay == SeekerWay.POINT_END){
                        this.kebabDirectEnd.remove(parentLinkedList); //裂变 1变N 后删除 1 保留N条副本
                    }else {
                        this.kebabDirectBegin.remove(parentLinkedList); //裂变 1变N 后删除 1 保留N条副本
                    }


                }
            }


    }





}
